

// printdpb.c 
//
// created by Mike Podanoffsky
//            "Dissecting DOS"
// 
// WARNING: This program will read the disk
// 
// USAGE:   printDPB x:
// 
// make instructions:
// 
//    cl /AL printdpb.c
// 
// 
// 

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <dos.h>

#define _RXDOS_bLastDrive           0x0047
static int DOSVersion = 5;

// DOS dpb Structure

#pragma pack(1)
typedef struct _v5DPB {

   char        _dpbDrive;
   char        _dpbUnit;
   int         _dpbBytesPerSector;
   char        _dpbClusterSizeMask;
   char        _dpbClusterSizeShift;
   int         _dpbFirstFAT;
   char        _dpbNumCopiesFAT;
   int         _dpbMaxAllocRootDir;
   int         _dpbFirstDataSector;
   int         _dpbMaxClusterNumber;

   int         _dpbSectorsPerFat;            /* int in version 5 */
   int         _dpbFirstDirSector;
   void      * _dpbptrDeviceDriver;
   char        _dpbMediaDescriptor;
   char        _dpbAccessFlag;
   struct _v5DPB * _dpbNextDPB;
   int         _dpbNextFree;
   int         _dpbFreeCount;

   } v5DPB, * LP_v5DPB;

typedef struct _v3DPB {

   char        _dpbDrive;
   char        _dpbUnit;
   int         _dpbBytesPerSector;
   char        _dpbClusterSizeMask;
   char        _dpbClusterSizeShift;
   int         _dpbFirstFAT;
   char        _dpbNumCopiesFAT;
   int         _dpbMaxAllocRootDir;
   int         _dpbFirstDataSector;
   int         _dpbMaxClusterNumber;

   char        _dpbSectorsPerFat;            /* byte in early versions */
   int         _dpbFirstDirSector;
   void      * _dpbptrDeviceDriver;
   char        _dpbMediaDescriptor;
   char        _dpbAccessFlag;
   struct _v3DPB * _dpbNextDPB;
   int         _dpbNextFree;
   int         _dpbFreeCount;


   } v3DPB, * LP_v3DPB;

#pragma pack()

///////////////////////////////////////////////////////////////
static int ShiftTable[ ] = 
      {  1, 2, 4, 8, 16, 32, 64, 128, 256 };
      
///////////////////////////////////////////////////////////////
void printDPB( int Drive )
{

   union _REGS r;
   struct _SREGS s;

   long      Clusters = 0L;
   long      SectorsPerCluster;
   long      DiskSize;

   void    * lpDPB;
   LP_v3DPB  lpv3DPB;
   LP_v5DPB  lpv5DPB;

   printf ( "\n" );
   printf ( " Drive Parameter Block %c:\n", Drive | 'A' );
   printf ( " ---------------------------------------------------\n" );

   r.h.ah = 0x32;         
   r.h.dl = Drive + 1;

   _intdosx( &r, &r, &s );             // get DOS version 

   if ( r.h.al == 0xFF )
      {
      printf( " No DPB for %c:\n\n", Drive | 'A' );
      return;
      }

   FP_SEG( lpDPB ) = s. ds;
   FP_OFF( lpDPB ) = r. x. bx;

   if ( DOSVersion == 5 )
      {
      lpv5DPB = (LP_v5DPB )lpDPB;

      (int )Clusters = lpv5DPB-> _dpbMaxClusterNumber;
      SectorsPerCluster = ShiftTable[ lpv5DPB-> _dpbClusterSizeShift ];

      DiskSize = Clusters * SectorsPerCluster * lpv5DPB-> _dpbBytesPerSector;

      printf ( "            Drive/Unit: %3x/%3x\n", 
                                    (int )lpv5DPB-> _dpbDrive,
                                    (int )lpv5DPB-> _dpbUnit         );

      printf ( "          Bytes/Sector: %d\n", 
                                     lpv5DPB-> _dpbBytesPerSector    );

      printf ( "       Sectors/Cluster: %ld\n", 
                                     SectorsPerCluster               );

      printf ( "          Max Clusters: %ld\n", 
                                     Clusters                        );

      printf ( "       Total Disk Size: %ld\n", 
                                     DiskSize                        );
      printf ( "\n" );


      printf ( "             First FAT: 0x%04x\n", 
                                    lpv5DPB-> _dpbFirstFAT           );

      printf ( "         NumCopies FAT: %d\n", 
                                    lpv5DPB-> _dpbNumCopiesFAT       );

      printf ( "       Sectors Per FAT: %d\n", 
                                    (int )lpv5DPB-> _dpbSectorsPerFat );
      printf ( "\n" );


      printf ( "    Max Alloc Root Dir: %d\n", 
                                     lpv5DPB-> _dpbMaxAllocRootDir );

      printf ( "     First Data Sector: 0x%04x\n", 
                                     lpv5DPB-> _dpbFirstDataSector );

      printf ( "      First Dir Sector: 0x%04x\n", 
                                    (int )lpv5DPB-> _dpbFirstDirSector );
      printf ( "\n" );

      printf ( "     ptr Device Driver: %04X:%04X\n", 
                                    FP_SEG( lpv5DPB-> _dpbptrDeviceDriver ),
                                    FP_OFF( lpv5DPB-> _dpbptrDeviceDriver ) );

      printf ( "      Media Descriptor: 0x%2x\n", 
                                     lpv5DPB-> _dpbMediaDescriptor   );

      printf ( "             Next Free: %d\n",
                                    (int )lpv5DPB-> _dpbNextFree     );

      printf ( "            Free Count: %d\n", 
                                    (int )lpv5DPB-> _dpbFreeCount    );
      printf ( "\n" );
      }


   else
      {
      lpv3DPB = (LP_v3DPB )lpDPB;

      (int )Clusters = lpv3DPB-> _dpbMaxClusterNumber;
      SectorsPerCluster = ShiftTable[ lpv3DPB-> _dpbClusterSizeShift ];

      DiskSize = Clusters * SectorsPerCluster * lpv3DPB-> _dpbBytesPerSector;

      printf ( "            Drive/Unit: %3x/%3x\n", 
                                    (int )lpv3DPB-> _dpbDrive,
                                    (int )lpv3DPB-> _dpbUnit         );

      printf ( "          Bytes/Sector: %d\n", 
                                     lpv3DPB-> _dpbBytesPerSector    );

      printf ( "       Sectors/Cluster: %ld\n", 
                                     SectorsPerCluster               );

      printf ( "          Max Clusters: %ld\n", 
                                     Clusters                        );

      printf ( "       Total Disk Size: %ld\n", 
                                     DiskSize                        );
      printf ( "\n" );


      printf ( "             First FAT: 0x%04x\n", 
                                    lpv3DPB-> _dpbFirstFAT           );

      printf ( "         NumCopies FAT: %d\n", 
                                    lpv3DPB-> _dpbNumCopiesFAT       );

      printf ( "       Sectors Per FAT: %d\n", 
                                    (int )lpv3DPB-> _dpbSectorsPerFat );
      printf ( "\n" );


      printf ( "    Max Alloc Root Dir: %d\n", 
                                     lpv3DPB-> _dpbMaxAllocRootDir );

      printf ( "     First Data Sector: 0x%04x\n", 
                                     lpv3DPB-> _dpbFirstDataSector );

      printf ( "      First Dir Sector: 0x%04x\n", 
                                    (int )lpv3DPB-> _dpbFirstDirSector );
      printf ( "\n" );

      printf ( "     ptr Device Driver: %04X:%04X\n", 
                                    FP_SEG( lpv3DPB-> _dpbptrDeviceDriver ),
                                    FP_OFF( lpv3DPB-> _dpbptrDeviceDriver ) );

      printf ( "      Media Descriptor: 0x%2x\n", 
                                     lpv3DPB-> _dpbMediaDescriptor   );

      printf ( "             Next Free: %d\n",
                                    (int )lpv3DPB-> _dpbNextFree     );

      printf ( "            Free Count: %d\n", 
                                    (int )lpv3DPB-> _dpbFreeCount    );
      printf ( "\n" );
      }

}


///////////////////////////////////////////////////////////////
main(int argc, char * argv[] )
{

   union _REGS r;
   struct _SREGS s;

   void * lpDPB;
   char  *lpLastDrive;
   int    k, LastDrive = 0;
   int    Drive = 0;

   // get DOS Version

   r.h.ah = 0x30;         
   _intdosx( &r, &r, &s );             // get DOS version 

   DOSVersion = ( r. h. al >= 0x5 ) ? 5 : 3;

   // get DOS Drives

   r.h.ah = 0x52;                      // use undoc function 52
   _intdosx( &r, &r, &s );             // es contains seg address of DOS

   FP_SEG( lpLastDrive ) = s.es;
   FP_OFF( lpLastDrive ) = _RXDOS_bLastDrive;
   LastDrive = (int )(*lpLastDrive );

   // looking at a specific drive ?

   if ( argc < 2 )
      {
      printf( "USAGE: printDPB x:\n" );
      exit(0);
      }

   // see if argument is valid

   Drive = toupper(*argv[ 1 ]) - 'A';

   if ( Drive < 0 || Drive > *lpLastDrive )
      {
      printf( "DPB: driver must be between A: and %c: \n", (*lpLastDrive | 'A') + 1 );
      exit(1);
      }

   printDPB( Drive );
   exit(0);
}

